use std::path::{PathBuf, Path};
use std::sync::{Arc, Mutex};
use rand::Rng;

mod context;

/// 导出接口
pub use context::Context;

/**
 * 在不同线程间共享的全局数据
 */
pub struct Config {
    pub data_path: PathBuf,
    pub web_path: PathBuf,
    pub host_web: bool,             // 是否托管前端页面
    pub do_init: bool,              // 是否初始化data目录

    pub parse_args: bool,
    pub load_config: bool,
}

/// 用户信息
pub struct User {
    id: u32,
    name: String,
    password: String,
    salt: String,
    create_date: i64,
    is_admin: bool,
    avatar: Option<Vec<u8>>,
    delete_date: Option<i64>,
}

impl User {
}

/// 用于设置user的值
pub enum UserValue {
    Name(String),
    IsAdmin(bool),
    Avatar(Vec<u8>),
}

impl std::default::Default for User {
    fn default() -> Self {
        User {
            id: 0,
            name: String::new(),
            password: String::new(),
            salt: String::new(),
            create_date: 0,
            is_admin: false,
            avatar: None,
            delete_date: None,
        }
    }
}

impl Config {
    /// 解析优先级：先设定默认的路径，然后再解析配置文件，之后解析命令行，最后解析参数
    pub fn create(parse_args: bool,
                  load_config: bool,
                  data_path: Option<PathBuf>,
                  web_path: Option<PathBuf>,
                  host_web: Option<bool>,
                  do_init: Option<bool>) -> Arc<Mutex<Config>> {
        // 默认设置
        let mut config = Config {
            data_path: Path::new("./data").to_path_buf(),
            web_path: Path::new("./web").to_path_buf(),
            host_web: false,
            do_init: false,

            parse_args,
            load_config,
        };

        // 从配置文件中读取
        if load_config {
            config.load_config();
        }

        // 从命令行参数中读取
        if parse_args {
            config.parse_args();
        }

        // 从参数中读取
        if let Some(path) = data_path {
            config.data_path = path;
        }
        if let Some(path) = web_path {
            config.web_path = path;
        }
        if let Some(b) = host_web {
            config.host_web = b;
        }
        if let Some(b) = do_init {
            config.do_init = b;
        }

        Arc::new(Mutex::new(config))
    }

    fn load_config(&mut self) -> bool {
        // TODO
        true
    }

    fn parse_args(&mut self) -> bool {
        // TODO
        true
    }
}

/*
#[derive(Debug)]
pub enum ERROR {
    WebPathMissing,
    DataPathMissing,
    DataPathNotInit,
    MutexError,
    DatabaseError,
    ExecuteError,
    QueryError,
}
*/


/// gulpereel错误信息
#[derive(Debug)]
pub enum Error {
    UnknownError(String),
    StorageError(String),
    LockError(String),
    DatabaseError(String),
}

impl Error {
}

impl std::fmt::Display for Error {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        // TODO: 输出错误信息
        write!(f, "{}", match self {
            Error::UnknownError(ref s) => String::from(s),
            Error::StorageError(ref s) => String::from(s),
            _ => String::from("Unknow error"),
        })
    }
}

impl std::error::Error for Error {}

impl From<rusqlite::Error> for Error {
    fn from(err: rusqlite::Error) -> Self {
        Error::DatabaseError(err.to_string())
    }
}

impl From<std::io::Error> for Error {
    fn from(err: std::io::Error) -> Self {
        Error::StorageError(err.to_string())
    }
}

impl From<std::string::FromUtf8Error> for Error {
    fn from(err: std::string::FromUtf8Error) -> Self {
        Error::UnknownError(err.to_string())
    }
}


/// 生成一个指定长度的随机字符串
fn gen_random_str(len: u32) -> String {
    let mut rand_str = String::new();
    let mut rng = rand::thread_rng();
    let mut rand_byte: u8;
    for _ in 0..len {
        // 随机生成一个从 '!' 到 '~' 的字符
        rand_byte = rng.gen_range(33, 127);
        rand_str.push(rand_byte.into());
    }
    rand_str
}


/// 获取当前时间戳，单位为秒
fn get_current_timestamp() -> i64 {
    use std::convert::TryInto;

    let secs = match std::time::SystemTime::now()
        .duration_since(std::time::SystemTime::UNIX_EPOCH) {
        Ok(t) => t.as_secs(),
        Err(_) => 0,
    };

    match secs.try_into() {
        Ok(s) => s,
        Err(_) => i64::max_value(),
    }
}